#include <stdio.h>
#include <stdlib.h>
#include "wblib.h"
#include "nuc900_gnand.h"
#include "nuc900_sic.h"

#define printf	sysprintf

NDRV_T _nandDiskDriver0 = 
{
	nandInit0,
	nandpread0,
	nandpwrite0,
	nand_is_page_dirty0,
	nand_is_valid_block0,
	nand_ioctl,
	nand_block_erase0,
	nand_chip_erase0,
	0
};

NDRV_T _nandDiskDriver1 = 
{
	nandInit1,
	nandpread1,
	nandpwrite1,
	nand_is_page_dirty1,
	nand_is_valid_block1,
	nand_ioctl,
	nand_block_erase1,
	nand_chip_erase1,
	0
};

#define FMI_TEST_SIZE	5 * 1024 *1024

int main()
{
	int volatile i, data, totalSec, sectorNo;
	unsigned char *pSrc, *pDst;
	int volatile btime, status, errFlag=0;
	WB_PLL_T sysClock;
	NDISK_T *ptNDisk;

	sysClock.pll0 = PLL_200MHZ;
	sysClock.pll1 = PLL_133MHZ;
	sysClock.cpu_src = CPU_FROM_PLL0;
	sysClock.ahb_clk = AHB_CPUCLK_1_2;
	sysClock.apb_clk = APB_AHB_1_2;
	sysSetPLLConfig(&sysClock);

#if 1
	sysDisableCache();
	sysInvalidCache();
	sysSetMMUMappingMethod(MMU_DIRECT_MAPPING);
	sysEnableCache(CACHE_WRITE_BACK);
#endif


	pSrc = (UINT8 *)(0x81000000);
	pDst = (UINT8 *)(0x81600000);

	sysSetTimerReferenceClock (TIMER0, 15000000);
	sysStartTimer (TIMER0, 100, PERIODIC_MODE);

	data = 0;
	for (i=0; i<FMI_TEST_SIZE; i++)
	{
		*(pSrc+i) = (data + i) & 0xff;
		if (((i+1) % 0xff) == 0)
			data++;
	}

	// debug!! initial GPIOH bit3 nIRQ3
	outpw(0xb000000c, inpw(0xb000000c) & 0xfeffffff);
	outpw(0xB8003054, 0x08);
	outpw(0xB8003058, 0x00);

	// initial FMI
	sicOpen();
	sicIoctl(SIC_SET_CLOCK, 200000, 0, 0);

	ptNDisk = (NDISK_T *)malloc(sizeof(NDISK_T));
	if (ptNDisk == NULL)
		printf("malloc error!!\n");
	
	GNAND_InitNAND(&_nandDiskDriver0, ptNDisk, TRUE);

#if 1
	totalSec = FMI_TEST_SIZE/512;
	printf("SM write\n");
	btime = sysGetTicks(TIMER0);
	status = GNAND_write(ptNDisk, 0, FMI_TEST_SIZE/512, pSrc);
	btime = sysGetTicks(TIMER0) - btime;
	if (btime == 0) btime = 1;

	printf("[%x] SM write speed %d KB/sec\n", status, (FMI_TEST_SIZE * 100) / (btime * 1024));

	printf("SM read\n");
	btime = sysGetTicks(TIMER0);
	status = GNAND_read(ptNDisk, 0, FMI_TEST_SIZE/512, pDst);
	btime = sysGetTicks(TIMER0) - btime;
	if (btime == 0) btime = 1;

	printf("[%x] SM read speed %d KB/sec\n", status, (FMI_TEST_SIZE * 100) / (btime * 1024));

	printf("compare\n");
	for (i=0; i<FMI_TEST_SIZE; i++)
	{
		if (*(pSrc+i) != *(pDst+i))
		{
			printf("error!! <%x> Src[%x], Dst[%x]\n", i, *(pSrc+i), *(pDst+i));
			break;
		}
	}
#else
	//sectorNo = ptNDisk->nPagePerBlock * ptNDisk->nBlockPerZone * ptNDisk->nZone - 1;
	sectorNo = 1000;
	while(1)
	{
		memset(pDst, 0xff, FMI_TEST_SIZE);
		printf("write %d sector\n", sectorNo);
		status = GNAND_write(ptNDisk, sectorNo, FMI_TEST_SIZE/512, pSrc);
		//if (status < 0)
			//printf("write error %x\n", status);

		status = GNAND_read(ptNDisk, sectorNo, FMI_TEST_SIZE/512, pDst);
		//if (status < 0)
			//printf("read error %x\n", status);

		//printf("compare\n");
		for (i=0; i<FMI_TEST_SIZE; i++)
		{
			if (*(pSrc+i) != *(pDst+i))
			{
				outpw(0xB8003058, 0x08);
				printf("error!! <%x> Src[%x], Dst[%x]\n", i, *(pSrc+i), *(pDst+i));
				errFlag = 1;
				break;
			}
		}
		//sectorNo -= FMI_TEST_SIZE/512;
		sectorNo += FMI_TEST_SIZE/512;
		if ((sectorNo > (ptNDisk->nPagePerBlock * ptNDisk->nBlockPerZone * ptNDisk->nZone - 1)) || (errFlag))
		//if ((sectorNo < 1000) || (errFlag))
			break;
	}
#endif

	printf("finish...\n");
	return 0;
}

extern unsigned int Image$$ZI$$Limit;

__value_in_regs struct R0_R3 {unsigned heap_base, stack_base, heap_limit, stack_limit;} 
    __user_initial_stackheap(unsigned int R0, unsigned int SP, unsigned int R2, unsigned int SL)
{
    struct R0_R3 config;

    config.heap_base = (unsigned int)&Image$$ZI$$Limit;
    config.stack_base = sysGetSdramSizebyMB()*0x100000; //Stack base;

/*
To place heap_base directly above the ZI area, use:
    extern unsigned int Image$$ZI$$Limit;
    config.heap_base = (unsigned int)&Image$$ZI$$Limit;
(or &Image$$region_name$$ZI$$Limit for scatterloaded images)

To specify the limits for the heap & stack, use e.g:
    config.heap_limit = SL;
    config.stack_limit = SL;
*/

    return config;
}


